#ifndef LGE_THREAD_H
#define LGE_THREAD_H

#include <core/lge_types.h>
#include <core/lge_uncopyable.h>
#include <functional>

#include <windows.h>

/**
  * \brief A utility typedef defining what a thread function is
  *
  * This rely on a TR1 feature, so MSVC 2008 SP1 at least is required.
  */
typedef std::tr1::function<void (void)> ThreadFunction;

/**
  * \brief Utility define to easily create a ThreadFunction
  */
#define createThreadFunction(func, ...) std::tr1::bind(func, __VA_ARGS__)

/**
  * \brief Utility function that returns the number of logical processors on
  *        this machine
  */
SizeT processorCount();

/**
  * \brief Sleep the current thread msec milliseconds
  */
void sleep(SizeT msec);

/**
  * \brief A thread class allowing user to create and spawn new threads
  */
class Thread : public Uncopyable
{
	/**
	  * \brief Underlying thread handle (OS dept)
	  */
	HANDLE mThreadHandle;

	/**
	  * \brief The function to launch in the new thread
	  */
	ThreadFunction mThreadFunction;

	/**
	  * \brief Hold whether the thread is started or not
	  */
	bool mIsStarted;

	/**
	  * \brief This is the wrapping function running user function
	  */
	static DWORD WINAPI threadWrappingFunction(void * threadInstance)
	{
		Thread * instance = (Thread*)threadInstance;
		instance->mThreadFunction();
		return 0;
	}

public:
	/**
	  * \brief Create a new thread
	  *
	  * \param function A user defined function. This can be created using the
	  *        createThreadFunction macro.
	  * \param start Specify whether the thread is created and started, or if
	  *        the user has to start the thread by using start() method.
	  */
	Thread(const ThreadFunction & function, bool start=true)
	: mThreadFunction(function), mIsStarted(start)
	{
		mThreadHandle = CreateThread(NULL, 0, &threadWrappingFunction, this, 
			start ? 0 : CREATE_SUSPENDED, NULL);
	}

	/**
	  * \brief Start the thread if not already started
	  *
	  * \return true on success, false otherwise. A failure can happen if the
	  *         thread is already started, or if the thread creation has failed
	  */
	bool start()
	{
		if (mThreadHandle == NULL)
			return false;

		if (ResumeThread(mThreadHandle) == -1)
			return false;
		
		mIsStarted = true;
		return true;
	}

	/**
	  * \brief Wait for the thread to exit
	  *
	  * \return true on success, false otherwise.
	  *
	  * This works only if the thread has been started. If not, it returns
	  * immediatly false. It can also return false if the thread creation
	  * failed.
	  */
	bool join()
	{
		if (mThreadHandle == NULL || !mIsStarted)
			return false;

		return (WaitForSingleObject(mThreadHandle, INFINITE) == 0);
	}

	/**
	  * \brief Wait for thread to exit and release all associated resources
	  */
	~Thread()
	{
		join();
		CloseHandle(mThreadHandle);
	}
};

#endif
